// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; using System.Xml.Serialization; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Interfaces; using LargoCommon.Localization; using LargoCommon.Midi; namespace LargoCommon.Music { /// /// Musical Track. /// [Serializable] public sealed class MusicalLine : IAbstractLine { #region Fields //// //// Last Used Bit. //// //// private int lastUsedBit; /// Complete list of all tones in musical part. private MusicalStrikeCollection tones; /// /// Musical Block. /// [NonSerialized] private MusicalStrip strip; /// Gets or sets corresponding MIDI track. /// Property description. private MidiEventCollection midiEventColl; #endregion #region Constructors /// /// Initializes a new instance of the MusicalLine class. /// public MusicalLine() { this.tones = new MusicalStrikeCollection(); //// this.IsNested = false; this.LineIdent = Guid.NewGuid(); this.StatusList = new List(); this.Purpose = LinePurpose.None; this.FirstStatus = new LineStatus { LocalPurpose = LinePurpose.None, Instrument = new MusicalInstrument(MidiMelodicInstrument.None) //// FixedInstrument }; this.MainVoice = new MusicalVoice { Octave = this.FirstStatus.Octave, Instrument = this.FirstStatus.Instrument, Loudness = this.FirstStatus.Loudness, Line = this }; } /// /// Initializes a new instance of the class. /// /// The given status. public MusicalLine(LineStatus givenStatus) { this.Reset(); this.tones = new MusicalStrikeCollection(); //// this.IsNested = false; this.LineIdent = Guid.NewGuid(); this.StatusList = new List(); this.FirstStatus = givenStatus ?? new LineStatus(); this.MainVoice = new MusicalVoice { Octave = this.FirstStatus.Octave, Instrument = this.FirstStatus.Instrument, Loudness = this.FirstStatus.Loudness, Line = this }; //// this.Purpose = this.Purpose; //// new MusicalInstrument(MidiMelodicInstrument.None); //// FixedInstrument } /// /// Initializes a new instance of the MusicalLine class. /// /// The given midi track. /// The given strip. public MusicalLine(IMidiTrack givenMidiTrack, MusicalStrip givenStrip) /* 2019/01 MusicalSection givenArea */ { Contract.Requires(givenMidiTrack != null); this.Name = givenMidiTrack.Name; this.TrackNumber = (byte)givenMidiTrack.TrackNumber; this.LineIdent = Guid.NewGuid(); this.Strip = givenStrip; this.StatusList = new List(); this.Purpose = LinePurpose.Fixed; this.FirstStatus = new LineStatus { Staff = givenMidiTrack.Staff, Voice = givenMidiTrack.Voice, LocalPurpose = LinePurpose.Fixed //// 2019/01 LinePurpose.Fixed }; this.MainVoice = new MusicalVoice { Octave = this.FirstStatus.Octave, Instrument = this.FirstStatus.Instrument, Loudness = this.FirstStatus.Loudness, Line = this, Channel = givenMidiTrack.Channel }; MusicalLineType lineType = MusicalLineType.None; if (givenMidiTrack.IsMelodic) { lineType = MusicalLineType.Melodic; this.FirstStatus.Instrument = new MusicalInstrument(givenMidiTrack.FirstMelodicInstrumentInEvents); } else if (givenMidiTrack.IsRhythmical) { lineType = MusicalLineType.Rhythmic; this.FirstStatus.Instrument = new MusicalInstrument(givenMidiTrack.FirstRhythmicInstrumentInEvents); } this.FirstStatus.LineType = lineType; var midiTones = new MidiTones(givenMidiTrack); /* 2019/01 givenArea */ if (midiTones.List.Count <= 0) { return; } this.AppendMidiTrackTones(midiTones); //// midiTones this.Purpose = LinePurpose.Fixed; } /// /// Initializes a new instance of the class. /// /// The mark track. /// The given header. public MusicalLine(XElement markTrack, MusicalHeader givenHeader) { Contract.Requires(markTrack != null); if (markTrack == null) { return; } byte rhythmicOrder = givenHeader.System.RhythmicOrder; this.LineType = DataEnums.ReadAttributeMusicalLineType(markTrack.Attribute("LineType")); this.Purpose = DataEnums.ReadAttributeLinePurpose(markTrack.Attribute("Purpose")); this.LineIndex = XmlSupport.ReadByteAttribute(markTrack.Attribute("LineIndex")); this.StatusList = new List(); this.FirstStatus = new LineStatus { LocalPurpose = DataEnums.ReadAttributeLinePurpose(markTrack.Attribute("Purpose")) }; var xstatusList = markTrack.Element("StatusList"); if (xstatusList != null) { var xstatusListElements = xstatusList.Elements(); var xtrackStatuses = xstatusListElements.ToList(); var xtrackStatus1 = xtrackStatuses.FirstOrDefault(); if (xtrackStatus1 != null) { this.FirstStatus.SetXElement(xtrackStatus1, givenHeader); } this.CurrentStatus = this.FirstStatus.Clone() as LineStatus; foreach (var xtrackStatus in xtrackStatuses) { //// var writtenStatus = new TrackStatus(xtrackStatus, givenHeader); if (this.CurrentStatus == null) { continue; } this.CurrentStatus.SetXElement(xtrackStatus, givenHeader); var status = this.CurrentStatus.Clone() as LineStatus; if (status == null) { continue; } status.BarNumber = XmlSupport.ReadIntegerAttribute(xtrackStatus.Attribute("Bar")); this.StatusList.Add(status); } var purpose = (from ts in this.StatusList where ts.LocalPurpose != LinePurpose.None select ts.LocalPurpose) .FirstOrDefault(); this.Purpose = purpose; } this.LineIdent = Guid.NewGuid(); this.MainVoice = new MusicalVoice { Octave = this.FirstStatus.Octave, Instrument = this.FirstStatus.Instrument, Loudness = this.FirstStatus.Loudness, Line = this }; var attribute = markTrack.Attribute("Channel"); if (attribute != null) { this.MainVoice.Channel = (MidiChannel)XmlSupport.ReadByteAttribute(attribute); } var xtones = markTrack.Element("Tones"); if (xtones != null) { var mts = new MusicalStrikeCollection(xtones, rhythmicOrder); this.SetTones(mts); } } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public XElement GetXElement { get { var xtrack = new XElement("Track", null); xtrack.Add(new XAttribute("LineIndex", this.LineIndex)); xtrack.Add(new XAttribute("LineType", this.LineType)); xtrack.Add(new XAttribute("Channel", (int)this.MainVoice.Channel)); xtrack.Add(new XAttribute("Purpose", this.Purpose)); //// xtrack.Add(new XAttribute("IsImported", this.IsImported)); //// xtrack.Add(this.FirstStatus.GetXElement); //// Status var statusList = this.StatusList; if (statusList != null) { XElement xstatusList = new XElement("StatusList"); LineStatus lastStatus = null; foreach (var ts in statusList) { var xstatus = lastStatus == null ? ts.GetXElement : ts.GetChangeXElement(lastStatus); xstatusList.Add(xstatus); lastStatus = ts; } xtrack.Add(xstatusList); } //// Music tones XElement xrealTones = new XElement("Tones"); var mtones = this.Tones; if (mtones != null) { byte lastInstrument = (byte)MidiMelodicInstrument.None; foreach (IMusicalTone mtone in mtones.Where(mtone => mtone != null)) { var xelement = mtone.GetXElement; if (mtone.InstrumentNumber != lastInstrument) { //// 2019/02 rt is MusicalStrike mtone && xelement.Add(new XAttribute("Instrument", mtone.InstrumentNumber)); lastInstrument = mtone.InstrumentNumber; } xrealTones.Add(xelement); } } xtrack.Add(xrealTones); return xtrack; } } #endregion #region Public Properties /// /// Gets or sets the type of the line. /// /// /// The type of the line. /// public MusicalLineType LineType { get { if (this.StatusList == null || this.FirstStatus == null) { return MusicalLineType.None; } return this.FirstStatus.LineType; } set { if (this.StatusList != null && this.FirstStatus != null) { this.FirstStatus.LineType = value; } } } /// Gets or sets purpose of the track. /// Property description. /// Returns value. public LinePurpose Purpose { get; set; } /// /// Gets or sets the track identifier. /// /// /// The track identifier. /// public Guid LineIdent { get; set; } /// Gets or sets line index - i.e. an unique mark of the track in the musical model. /// Property description. /// Returns value. public int LineIndex { get; set; } /// /// Gets the line number. /// /// /// The line number. /// public byte LineNumber => (byte)(this.LineIndex + 1); /// /// Gets or sets the current instrument. /// /// /// The current instrument. /// public byte CurrentInstrument { get; set; } /// /// Gets a value indicating whether this instance has content. /// /// /// true if this instance has content; otherwise, false. /// [UsedImplicitly] public bool HasContent => this.Purpose != LinePurpose.None && this.Purpose != LinePurpose.Mute; /// /// Gets or sets the current status. /// /// /// The current status. /// public LineStatus FirstStatus { get { Contract.Ensures(Contract.Result() != null); var firstStatus = this.StatusList.FirstOrDefault(); if (firstStatus == null) { throw new InvalidOperationException("First status is null."); } return firstStatus; } set => this.StatusList = new List { value }; } /// /// Gets or sets the current status. /// /// /// The current status. /// public LineStatus CurrentStatus { get; set; } /// /// Gets or sets the status list. /// /// /// The status list. /// public List StatusList { get; set; } /// /// Gets or sets track engine MusicalTrackEngine. /// /// Property description. public object TrackEngine { get; set; } /// /// Gets or sets the name. /// /// /// The track name. /// public string Name { get; set; } /// Gets or sets track number - original midi-track number. /// Property description. /// Returns value. public byte TrackNumber { [UsedImplicitly] get; set; } /// /// Gets corresponding MIDI track. /// /// /// Property description. /// public MidiEventCollection MidiEventCollection { get { if (this.midiEventColl == null || this.midiEventColl.Channel != this.MainVoice.Channel) { this.midiEventColl = new MidiEventCollection(this.MainVoice.Channel); } return this.midiEventColl; } } /// Gets a value indicating whether of empty musical part. /// Property description. /// Returns value. public bool IsEmpty { get { if (this.Tones == null || this.Tones.Count == 0) { return true; } var realTones = from t in this.Tones where t.ToneType != MusicalToneType.Empty && !t.IsEmpty select 1; return !realTones.Any(); } } /// /// Gets or sets musical block. /// /// Property description. public MusicalStrip Strip { get { Contract.Ensures(Contract.Result() != null); if (this.strip == null) { throw new InvalidOperationException("Musical strip is null."); } return this.strip; } set => this.strip = value ?? throw new ArgumentException(LocalizedMusic.String("Argument cannot be null."), nameof(value)); } /// /// Gets or sets a value indicating whether this instance is selected. /// /// /// true if this instance is selected; otherwise, false. /// public bool IsSelected { get; set; } /// /// Gets complete list of all tones in musical part. /// /// /// Property description. /// [XmlIgnore] public MusicalStrikeCollection Tones { get { Contract.Ensures(Contract.Result() != null); return this.tones ?? (this.tones = new MusicalStrikeCollection()); } } #endregion /// Gets or sets the system length. /// The length of the system. public int SystemLength { get; set; } /// Gets or sets the main voice. /// The main voice. public IAbstractVoice MainVoice { get; set; } /// Gets or sets the voices. /// The voices. public List Voices { get => new List { this.MainVoice }; set { } } /// /// Gets a list of identifiers. /// /// /// A list of identifiers. /// public IList Identifiers { get { var items = new List(); var item = new KeyValuePair("Line type", this.LineType.ToString()); items.Add(item); item = new KeyValuePair("Line number", this.LineNumber.ToString()); items.Add(item); item = new KeyValuePair("Purpose", this.Purpose.ToString()); items.Add(item); item = new KeyValuePair("Octave", this.MainVoice.Octave.ToString()); items.Add(item); item = new KeyValuePair("Loudness", this.MainVoice.Loudness.ToString()); items.Add(item); item = new KeyValuePair("Rhythmic tension", this.FirstStatus.RhythmicTension.ToString()); items.Add(item); item = new KeyValuePair(LocalizedMusic.String("Channel"), this.MainVoice.Channel.ToString()); items.Add(item); item = new KeyValuePair("Instrument", this.MainVoice.Instrument.ToString()); items.Add(item); return items; } } #region Tones /// Gets total number of not empty tones in this part. /// Property description. /// Returns value. [UsedImplicitly] public int NumberOfTones { get { //// .Cast() var num = (from tone in this.Tones where tone != null //// && melTone.Pitch != null select 1) .Count(); return num; } } /// Gets total number of not empty tones in this part. /// Property description. /// Returns value. [UsedImplicitly] public long DurationOfTones { get { //// .Cast() long num = (from melTone in this.Tones where melTone != null //// && melTone.Pitch != null select melTone.Duration) .Sum(); return num; } } /// /// Gets the number of tones. /// /// Property description. [UsedImplicitly] public int NumberOfAllTones => this.Tones.Count; #endregion #region Tone history /// Gets or sets current melodic tone. /// Property description. /// [XmlIgnore] public MusicalTone CurrentTone { get; set; } /// Gets or sets last melodic tone. /// Property description. /// [XmlIgnore] public MusicalTone LastTone { get; set; } /// Gets or sets last but one melodic tone. /// Property description. /// [XmlIgnore] public MusicalTone PenultTone { get; set; } #endregion #region Meta Texts /// /// Gets Midi Meta Text. /// /// General musical property. /// Returns value. public string MidiMetaText { get { var s = "*** TRACK ***" + this.LineIndex.ToString(CultureInfo.CurrentCulture); //// if (this.LineIndex == 1) { //// if (this.StatusChanged && this.Status != null) { //// s = this.AppendMetaTrackProperties(s); //// } return s; } } #endregion #region Private Properties /// Gets or sets file name. /// Property description. /// Returns value. private byte Multiplicity { get; set; } #endregion #region Static factory methods /// /// Gets the new musical track. /// /// Type of the line. /// The musical block. /// /// Returns value. /// public static MusicalLine GetNewMusicalLine(MusicalLineType lineType, MusicalBlock musicalBlock) { var firstStatus = new LineStatus() { LocalPurpose = LinePurpose.None, LineType = lineType //// channel == MidiChannel.DrumChannel ? MusicalLineType.Rhythmic : MusicalLineType.Melodic }; var track = new MusicalLine(firstStatus) { Strip = musicalBlock.Strip, Purpose = LinePurpose.None }; return track; } #endregion #region Public Methods /// /// Sets the tones. /// /// The given tones. public void SetTones(MusicalStrikeCollection givenTones) { this.tones = givenTones; } /// /// Sets the tones. /// /// The given tones. /// The given bar number. [UsedImplicitly] public void SetTones(MusicalStrikeCollection givenTones, int givenBarNumber) { this.tones = givenTones; foreach (var tone in this.tones) { tone.BarNumber = givenBarNumber; } } /// /// Sets the midi event collection. /// /// The collection. public void SetMidiEventCollection(MidiEventCollection collection) { this.midiEventColl = collection; } /// /// Reset musical track. /// /// The musical strip. /// Clear Tones. public void Reset(MusicalStrip musicalStrip, bool clearTones) { //// MusicalRules musicalRules, this.Strip = musicalStrip; //// this.tones = new MusicalStrikeCollection(); this.FirstStatus.MelodicPlan = new MelodicPlan(); if (clearTones) { this.Reset(); } } /// Makes a deep copy of the MusicalLine object. /// Returns object. public object Clone() { //// this.LineIndex/0 var status = (LineStatus)this.FirstStatus.Clone(); var track = new MusicalLine(status) { Name = this.Name, LineIndex = this.LineIndex, TrackNumber = this.TrackNumber, Strip = this.Strip, Multiplicity = this.Multiplicity, TrackEngine = this.TrackEngine }; track.Reset(); return track; } /// /// Clones the specified include tones. /// /// If set to true [include tones]. /// Returns value. public MusicalLine Clone(bool includeTones) { var track = (MusicalLine)this.Clone(); if (includeTones) { track.Tones.AddCollection(this.Tones.Clone(false), false); } return track; } /// /// Resets this instance. /// public void Reset() { this.tones = new MusicalStrikeCollection(); } #endregion #region Public Methods - Characteristics /// /// Quotients the of occupation. /// /// Returns value. public float QuotientOfOccupation() { var cnt = 0; var sum = 0; var header = this.Strip.Context.Header; for (var barNumber = 1; barNumber <= header.NumberOfBars; barNumber++) { var occupation = this.OccupationOfBar(header.System.RhythmicOrder, barNumber); var barSum = (from v in occupation where v > 0 select v).Sum(); var barCount = (from v in occupation where v > 0 select v).Count(); sum += barSum; cnt += barCount; } var value = ((float)sum) / cnt; return value; } /// Returns number of tones with given musical pitch. /// Musical pitch. /// Returns value. [UsedImplicitly] public byte CountOfUsedPitch(MusicalPitch pitch) { Contract.Requires(pitch != null); var num = (from melTone in this.Tones .Cast() where melTone.Pitch != null && melTone.Pitch.IsEqualTo(pitch) select 1) .Count(); return (byte)num; } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.AppendFormat(CultureInfo.CurrentCulture, "{0,4}, ", this.LineIndex); //\r\n\ s.Append(this.FirstStatus.LineType); //// s.Append(this.OctaveString); //// s.Append(this.BandTypeString); //// s.Append(this.InstrumentString); //// if (this.MusicalTones == null) { return s.ToString(); } var firstTone = this.Tones.FirstOrDefault(); if (firstTone != null) { s.Append(string.Format(CultureInfo.InvariantCulture, "#Bar{0}#", firstTone.BarNumber)); } s.AppendLine(string.Empty); s.Append(this.Tones.TonesToString()); s.AppendFormat("Ident={0}", this.LineIdent); //// this.TonesToString(s); return s.ToString(); } #endregion #region Public Methods - Load from Midi Lines /// /// Append MidiTrack Tones. /// /// The midi tones. public void AppendMidiTrackTones(IMidiTones midiTones) { if (midiTones == null) { return; } this.FirstStatus.LineType = midiTones.IsRhythmical ? MusicalLineType.Rhythmic : MusicalLineType.Melodic; //// musicalTrack.Instrument = midiTrack.Instrument; musicalTrack.Channel = midiTrack.Channel; //// Collection instrument = midiTrack.ReadRhythmicInstruments(); //// if (instrument != null) { musicalTrack.Instrument = (byte)instrument.FirstOrDefault(); } //// this.IsImported = true; this.Name = midiTones.Name; this.FirstStatus.Octave = midiTones.Octave; this.FirstStatus.BandType = midiTones.BandType; //// MusicalPitch.BandTypeFromOctave(musicalTrack.MusicalOctave); if (this.Strip.Context.Header.Metric.MetricGround > 0) { //// && midiTrack.Sequence != null var ts = MusicalStrikeCollection.GetTones(this.Strip.Context.Header, midiTones, this.FirstStatus.IsMelodic); this.SetTones(ts); } } #endregion #region Public Methods - Transformation /// /// Total Horizontal Inversion. /// public void TotalHorizontalInversion() { if (this.Tones == null || this.Tones.Count == 0) { return; } var mtc = new MusicalStrikeCollection(); var lastMusTone = this.Tones.LastOrDefault(); if (lastMusTone != null) { var extraBarNumber = lastMusTone.BarNumber + 1; foreach (var mtone in this.Tones) { if (!(mtone is MusicalStrike tone)) { continue; } tone.BarNumber = extraBarNumber - tone.BarNumber; var p = tone.IsFromPreviousBar; tone.IsFromPreviousBar = tone.IsGoingToNextBar; tone.IsGoingToNextBar = p; mtc.Insert(0, tone); } } this.tones = mtc; } /// /// Bar Horizontal Inversion. /// public void BarHorizontalInversion() { if (this.Tones == null || this.Tones.Count == 0) { return; } var mtc = new MusicalStrikeCollection(); var lastMusTone = this.Tones.LastOrDefault(); if (lastMusTone == null) { return; } var lastBarNumber = lastMusTone.BarNumber; for (var barNum = 1; barNum <= lastBarNumber; barNum++) { var barTones = this.MusicalTonesInBar(barNum); switch (barTones.Count) { case 0: continue; case 1: if (barTones[0] is MusicalStrike mtone) { mtone.IsGoingToNextBar = false; mtone.IsFromPreviousBar = false; mtc.Add(mtone); } continue; } DoInversion(barTones, mtc); } this.tones = mtc; } /// /// Divides the tones. /// /// The factor number. [UsedImplicitly] public void DivideTones(byte factorNumber) { var list = this.Tones.ToList(); //// Introduce foreach action - DefExpress foreach (var tn in list) { var tone = tn as MusicalStrike; if (tone != null && (tone.IsPause || tone.Duration % factorNumber != 0)) { continue; } if (tone != null) { var d = (byte)(tone.BitRange.Length / factorNumber); tone.BitRange = new BitRange(tone.BitRange.Order, tone.BitRange.BitFrom, d); tone.Duration = d; MusicalStrike newTone = null; for (var i = 1; i < factorNumber; i++) { newTone = (MusicalStrike)tone.CloneTone(); newTone.IsGoingToNextBar = false; newTone.IsFromPreviousBar = false; newTone.BitRange = new BitRange(tone.BitRange.Order, (byte)(tone.BitRange.BitFrom + (i * d)), d); this.Tones.Add(newTone); } if (newTone != null) { newTone.IsGoingToNextBar = tone.IsGoingToNextBar; } } if (tone != null) { tone.IsGoingToNextBar = false; } } var sortedTones = from mt in this.Tones orderby mt.BitPosition select mt; this.tones = new MusicalStrikeCollection(sortedTones.ToList()); } #endregion #region Public Methods - Manipulation with tones /// Gets or sets previous melodic tone. /// Property description. /// Melodic tone. /// Returns value. [UsedImplicitly] public MusicalTone PreviousTone(MusicalStrike givenTone) { Contract.Requires(givenTone != null); MusicalTone tone = null; //// if (givenTone == null) { return false; } var idx = givenTone.OrdinalIndex; //// this.MusicalTones.IndexOf(givenTone); if (idx <= 0) { return null; } for (var i = idx - 1; i >= 0; i--) { if (i >= this.Tones.Count) { continue; } tone = this.Tones[i] as MusicalTone; if (tone != null && tone.IsTrueTone) { //// Loudness > 0 return tone; //// Avoid multiple or conditional return statements. } } return tone; } /// List of tones in one bar of musical part. /// Musical area. /// Returns value. public MusicalStrikeCollection MusicalTonesInArea(MusicalSection givenArea) { Contract.Ensures(Contract.Result() != null); var list = from mT in this.Tones where mT.BarNumber >= givenArea.BarFrom && mT.BarNumber <= givenArea.BarTo select mT; var mtc = new MusicalStrikeCollection(list.ToList()); //// 2015/01 var newTone = (from mT in this.Tones where mT.BarNumber == givenArea.BarTo + 1 && mT.IsFromPreviousBar select mT).FirstOrDefault(); if (newTone != null) { mtc.AddTone(newTone, true); } return mtc; } /// List of tones in one bar of musical part. /// Number of musical bar. /// Returns value. public MusicalStrikeCollection MusicalTonesInBar(int barNumber) { Contract.Ensures(Contract.Result() != null); var list = from mT in this.Tones where mT.BarNumber == barNumber select mT; var mtc = new MusicalStrikeCollection(list.ToList()); return mtc; } /// List of tones in one bar of musical part. /// Number of musical bar. /// Returns value. public MusicalToneCollection MelodicTonesInBar(int barNumber) { var list = from mT in this.Tones where mT.BarNumber == barNumber && mT.ToneType == MusicalToneType.Melodic select mT; var mtc = new MusicalToneCollection(); foreach (var mtone in list.OfType().Where(mtone => !mtone.IsEmpty)) { mtc.Add(mtone); } return mtc; } /// /// Returns musical tone at given bar and tick in this part. /// /// Number of musical bar. /// Rhythmical tick. /// The rhythmic structure. /// /// Returns value. /// [UsedImplicitly] public MusicalStrike MusicalToneAt(int barNumber, byte tick, FiguralStructure rhythmicStructure) { //// RhythmicStructure var list = this.MusicalTonesInBar(barNumber); if (rhythmicStructure == null) { return null; } var level = rhythmicStructure.LevelOfBit(tick); return (level < list.Count ? list[level] : null) as MusicalStrike; } /// Add on musical tone to the end of part. /// Musical tone. public void AddMusicalTone(MusicalStrike givenTone) { Contract.Requires(givenTone != null); //// if (givenTone == null) { return false; } givenTone.OrdinalIndex = this.Tones.Count; this.Tones.Add(givenTone); } #endregion #region Private static methods /// /// Does the inversion. /// /// The bar tones. /// The tones. private static void DoInversion(IList barTones, ICollection tones) { Contract.Requires(barTones != null); Contract.Requires(tones != null); byte bit = 0; IMusicalTone mtoneA = null, mtoneB = null; var tonePosition = barTones.Count; while (--tonePosition >= 0) { var mtone0 = barTones[tonePosition]; if (mtone0 == null) { continue; } if (mtone0.IsPause && tonePosition > 0) { var mtone1 = mtone0; mtone0 = barTones[tonePosition - 1]; //// swap tones followed by a short pause if (mtone0 != null) { if (!mtone0.IsPause && mtone1.IsPause && mtone0.Duration > mtone1.Duration) { mtoneA = mtone0; mtoneB = mtone1; } else { mtoneA = mtone1; mtoneB = mtone0; } } if (mtoneA != null) { mtoneA.BitFrom = bit; mtoneA.IsGoingToNextBar = false; mtoneA.IsFromPreviousBar = false; //// mtoneA.BitTo = (byte)(bit + mtoneA.Duration - 1); bit = (byte)(mtoneA.BitTo + 1); tones.Add(mtoneA); mtoneB.BitFrom = bit; mtoneB.IsGoingToNextBar = false; mtoneB.IsFromPreviousBar = false; //// mtoneB.BitTo = (byte)(bit + mtoneB.Duration - 1); bit = (byte)(mtoneB.BitTo + 1); tones.Add(mtoneB); } tonePosition--; } else { mtone0.BitFrom = bit; mtone0.IsGoingToNextBar = false; mtone0.IsFromPreviousBar = false; //// mtone0.BitTo = (byte)(bit + mtone0.Duration - 1); bit = (byte)(mtone0.BitTo + 1); tones.Add(mtone0); } } } #endregion #region Private methods /// /// Occupations the of bar. /// /// The rhythmic order. /// The number of bar. /// /// Returns value. /// private int[] OccupationOfBar(byte rhythmicOrder, int barNumber) { var occupation = new int[rhythmicOrder]; for (byte tick = 0; tick < rhythmicOrder; tick++) { occupation[tick] = 0; } var tonesInBar = this.MusicalTonesInBar(barNumber); foreach (var tone in tonesInBar) { for (var tick = tone.BitFrom; tick <= tone.BitTo; tick++) { occupation[tick] += 1; } } return occupation; } #endregion } }